/**
 * Copyright (c) 2018, dreamkaylee@foxmail.com All Rights Reserved.
 */
  
package configuration;

import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.ibatis.plugin.Interceptor;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.context.annotation.FilterType;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.wall.WallConfig;
import com.alibaba.druid.wall.WallFilter;
import com.github.pagehelper.PageInterceptor;

/**
 * RootConfig
 * @author limk
 * @date 2018年2月12日 下午9:20:07
 * @version 1.0
 */
@Configuration
@ComponentScan(basePackages = {"com.limk.jconfig"},
        includeFilters = {@Filter(type = FilterType.ANNOTATION, value = Service.class)})
@MapperScan(basePackages = {"com.limk.jconfig.mapper"}, sqlSessionFactoryRef = "sqlSessionFactory")
@PropertySource(value = {"classpath:config/db.properties"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class RootConfig {
	
	@Value(value = "${db.driver}")
	private String driver;
	@Value(value = "${db.url}")
	private String url;
	@Value(value = "${db.username}")
	private String username;
	@Value(value = "${db.password}")
	private String password;
	
	/**
	 * 解决@Value(value = "${xx}")无法取值的问题
	 * @return
	 */
	@Bean
	public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
		return new PropertySourcesPlaceholderConfigurer();
	}
	
	/**
	 * druid数据源
	 * @return
	 * @throws SQLException
	 */
	@Bean(initMethod = "init", destroyMethod = "close")
	public DataSource druidDataSource() throws SQLException {
		DruidDataSource dataSource = new DruidDataSource();
		dataSource.setDriverClassName(driver);
		dataSource.setUrl(url);
		dataSource.setUsername(username);
		dataSource.setPassword(password);
		// 数据库类型
		dataSource.setDbType("mysql");
		// 初始化连接数量
		dataSource.setInitialSize(5);
		// 最大并发连接数
		dataSource.setMaxActive(20);
		// 最小空闲连接数
		dataSource.setMinIdle(5);
		// 配置获取连接等待超时的时间 
		dataSource.setMaxWait(60000);
		// 超过时间限制是否回收
		dataSource.setRemoveAbandoned(true);
		// 超过时间限制多长
		dataSource.setRemoveAbandonedTimeout(180);
		// 配置间隔多久才进行一次检测，检测需要关闭的空闲连接，单位是毫秒
		dataSource.setTimeBetweenEvictionRunsMillis(60000);
		// 配置一个连接在池中最小生存的时间，单位是毫秒
		dataSource.setMinEvictableIdleTimeMillis(300000);
		// 用来检测连接是否有效的sql，要求是一个查询语句
		dataSource.setValidationQuery("SELECT 1 FROM DUAL");
		// 申请连接的时候检测
		dataSource.setTestWhileIdle(true);
		// 申请连接时执行validationQuery检测连接是否有效，配置为true会降低性能
		dataSource.setTestOnBorrow(false);
		// 归还连接时执行validationQuery检测连接是否有效，配置为true会降低性能
		dataSource.setTestOnReturn(false);
		// 打开PSCache，并且指定每个连接上PSCache的大小,Oracle和MySQL5.5以上开启
		dataSource.setPoolPreparedStatements(true);
		dataSource.setMaxPoolPreparedStatementPerConnectionSize(20);
		// 属性类型是字符串，通过别名的方式配置扩展插件，常用的插件有： 监控统计用的filter:stat 日志用的filter:log4j 防御SQL注入的filter:wall
		dataSource.setFilters("stat,wall,log4j2");
		// 通过connectProperties属性来打开mergeSql功能；慢SQL记录
		Properties properties = new Properties();
		properties.setProperty("druid.stat.mergeSql", "true");
		properties.setProperty("druid.stat.slowSqlMillis", "5000");
		dataSource.setConnectProperties(properties);
		
		// 允许多个批量处理配置, 修改后存在SQL注入风险
		WallConfig config = new WallConfig();
		config.setMultiStatementAllow(true);
		WallFilter wallFilter = new WallFilter();
		wallFilter.setConfig(config);
		List<com.alibaba.druid.filter.Filter> filters = new ArrayList<>();
		filters.add(wallFilter);
		dataSource.setProxyFilters(filters);
		return dataSource;
	}
	
	/**
	 * mybatis的配置
	 * @return
	 * @throws SQLException
	 * @throws IOException
	 */
	@Bean(name = "sqlSessionFactory")
	public SqlSessionFactoryBean sqlSessionFactoryBean() throws SQLException, IOException {
		SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
		factory.setDataSource(druidDataSource());  
//		factory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:com/limk/jconfig/mapper/*/*.xml"));
		factory.setTypeAliasesPackage("com.limk.jconfig.entity");
		
		// 设置MyBatis分页插件
        PageInterceptor pageInterceptor = this.initPageInterceptor();
        factory.setPlugins(new Interceptor[]{pageInterceptor});
        return factory;
	}
	
	public PageInterceptor initPageInterceptor(){
        PageInterceptor pageInterceptor = new PageInterceptor();
        Properties properties = new Properties();
        properties.setProperty("helperDialect", "mysql");
		properties.setProperty("reasonable", "true");
		properties.setProperty("supportMethodsArguments", "true");
		properties.setProperty("autoRuntimeDialect", "true");
        pageInterceptor.setProperties(properties);
        return pageInterceptor;
    }
	
	/**
	 * sqlSession
	 * @return
	 * @throws Exception 
	 * @throws IOException 
	 * @throws SQLException 
	 */
	@Bean(name = "sqlSession")
	public SqlSessionTemplate sqlSession() throws SQLException, IOException, Exception {
		SqlSessionTemplate sqlSession = new SqlSessionTemplate(sqlSessionFactoryBean().getObject());
		return sqlSession;
	}
	
	/**
	 * 事务
	 * @return
	 * @throws SQLException
	 */
	@Bean(name = "transactionManager")
	public DataSourceTransactionManager transactionManager() throws SQLException {
		DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
		transactionManager.setDataSource(druidDataSource());
		return transactionManager;
	}
	
}
  